#include "../include/SerialPort.hpp"
#include "../include/Packet.hpp"
#include "../include/util.hpp"


SerialPort::SerialPort(void){
    Packet_RXD_ptr = 0;
}

SerialPort::~SerialPort(void) {
    stop();
}


void SerialPort::set_packet_rxd_ptr(boost::shared_ptr<Packet> * Packet_RXD_ptr) {
    this->Packet_RXD_ptr = Packet_RXD_ptr;
}


bool SerialPort::start(const char *com_port_name, int baud_rate) {
    boost::system::error_code ec;

    if (port_) {
        std::cout << "error : port is already opened..." << std::endl;
        return false;
    }

    port_ = serial_port_ptr(new serial_port(io_service_));
    port_->open(com_port_name, ec);
    if (ec) {
        std::cout << "error : port_->open() failed...com_port_name="
            << com_port_name << ", e=" << ec.message().c_str() << std::endl;
        return false;
    }


    port_->set_option(boost::asio::serial_port_base::baud_rate(baud_rate));
    port_->set_option(boost::asio::serial_port_base::character_size(8));
    port_->set_option(boost::asio::serial_port_base::stop_bits(boost::asio::serial_port_base::stop_bits::one));
    port_->set_option(boost::asio::serial_port_base::parity(boost::asio::serial_port_base::parity::none));
    port_->set_option(boost::asio::serial_port_base::flow_control(boost::asio::serial_port_base::flow_control::none));

    boost::thread t(boost::bind(&boost::asio::io_service::run, &io_service_));

    async_read();

    return (true);
}



void SerialPort::stop() {
    if (port_) {
        port_->cancel();
        port_->close();
        port_.reset();
    }
    io_service_.stop();
    io_service_.reset();
}



int SerialPort::write(const unsigned char buf) {
    return (write(buf, sizeof (unsigned char)));
}



int SerialPort::write(const unsigned char buf, const unsigned char size) {
    boost::system::error_code ec;

    if (!port_) {
        return (-1);
    }

    if (size == 0) {
        return (0);
    }

    return (port_->write_some(boost::asio::buffer(&buf, (unsigned int)size), ec));
}



void SerialPort::async_read() {

    if (port_.get() == NULL || !port_->is_open()) {
        return;
    }

    port_->async_read_some(
        boost::asio::buffer(read_buf_raw_, SERIAL_PORT_READ_BUF_SIZE),
        boost::bind(
            &SerialPort::on_receive,
            this, boost::asio::placeholders::error,
            boost::asio::placeholders::bytes_transferred));
}



void SerialPort::on_receive(const boost::system::error_code& ec, size_t bytes_transferred) {

    if (port_.get() == NULL || !port_->is_open()) {
        return;
    }

    if (ec) {
        async_read();
        return;
    }

    /* Copy from the raw data buffer that was passed in. */
    for (uint32_t j = 0; j < bytes_transferred; ++j) {
        Packet_RXD_ptr->get()->get_data()->push_back(read_buf_raw_[j]);
    }

    async_read();
}
